#pragma once
#ifndef _LINAL_CSRMATRIX_
#define _LINAL_CSRMATRIX_

//================================================================================================================================
//================================================================================================================================
// QtCore
#include <QtCore/QVector>
// Локальные
#include "math/math.h"
#include "linal/vector.h"
#include "core_global.h"

//================================================================================================================================
//================================================================================================================================
namespace linal
{
    /// Матрица в разреженно-строчном формате.
    /// \sa Метод конечных элементов для решения скалярных и векторных задач : учеб. пособие / Ю.Г. Соловейчик, М.Э. Рояк,
    /// М.Г. Персова - Новосибирск: Изд-во НГТУ, 2007. - 896 с.
    class CORE_EXPORT CSRMatrix
    {
    public:
        /// Создание матрицы нулевой размерности.
        CSRMatrix() : m_Dimension(0), m_igl(0), m_jgl(0), m_igu(0), m_ngu(0), m_di(0), m_ggl(0), m_ggu(0) {}

        /// Создание нулевой матрицы размерности \e dimension с заданным портретом.
        /// \param dimension Размерность матрицы.
        /// \param ig Информация о количестве ненулевых элементов в каждой строке матрицы.
        /// \param jg Информация о номерах столбцов ненулевых элементов матрицы.
        CSRMatrix(int dimension, const QVector<int> &igl, const QVector<int> &jgl, const QVector<int> &igu, const QVector<int> &ngu) :
            m_Dimension(dimension), m_igl(igl), m_jgl(jgl), m_igu(igu), m_ngu(ngu), m_di(dimension, 0.), m_ggl(igl.last(), 0.), m_ggu(igl.last(), 0.) {}

        /// Изменить размерность и портрет матрицы.
        /// \remarks Данная опреция обнуляет матрицу (присваивает всем элементам матрицы нулевое значение).
        /// \param dimension Размерность матрицы.
        /// \param ig Информация о количестве ненулевых элементов в каждой строке матрицы.
        /// \param jg Информация о номерах столбцов ненулевых элементов матрицы.
        void setMatrixPortrait(int dimension, const QVector<int> &igl, const QVector<int> &jgl, const QVector<int> &igu, const QVector<int> &ngu)
        {
            m_Dimension = dimension;
            m_igl = igl;
            m_jgl = jgl;
            m_igu = igu;
            m_ngu = ngu;
            m_di.fill(0., dimension);
            m_ggl.fill(0., igl.last());
            m_ggu.fill(0., igl.last());

        }

        /// Обнулить все элементы матрицы (присвоить им нулевое значение).
        /// \remarks Функция создана для обнуления матрицы перед началом сборки СЛАУ для МКЭ.
        void zeroize() {m_di.fill(0.); m_ggl.fill(0.); m_ggu.fill(0.);}

        /// Сделать матрицу единичной.
        //
        /// Все элементы диагонали приравниваются к единице, а верхний и нижний треугольники обнуляются.
        /// Портрет матрицы сохраняется.
        void makeIdentity() {m_di.fill(1.); m_ggl.fill(0.), m_ggu.fill(0.);}

        /// Указанную строку матрицы превратить в строку единичной матрицы.
        //
        /// Диагональный элемент указанной строки становится равным единице, а все остальные элементы строки зануляются.
        /// При этом портрет матрицы не изменяется.
        void makeIdentity(int row);

        /// Получить размерность матрицы.
        int dimension() const {return m_Dimension;}

        /// Транспонировать матрицу.
        void transpose() {math::permute(m_ggl, m_ggu);}

        /// Получить матрицу, полученную из данной с помощью транспонирования.
        CSRMatrix transposed() const
        {
            CSRMatrix result(m_Dimension, m_igl, m_jgl, m_igu, m_ngu);
            result.m_di = m_di;
            result.m_ggl = m_ggl;
            result.m_ggu = m_ggu;
            return result;
        }

        /// Получение информацию о количестве ненулевых элементов в каждой строке матрицы.
        /// \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        /// внутренней структуре матрицы.
        const QVector<int>& ig() const {return m_igl;}

        /// Получение информацию о номерах столбцов ненулевых элементов матрицы.
        /// \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        /// внутренней структуре матрицы.
        const QVector<int>& jg() const {return m_jgl;}

        /// Получение данных (в формате CSR) о диагонали матрицы.
        /// \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        /// внутренней структуре матрицы.
        const QVector<double>& di() const {return m_di;}

        /// Получение данных (в формате CSR) о нижнем треугольнике матрицы.
        /// \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        /// внутренней структуре матрицы.
        const QVector<double>& ggl() const {return m_ggl;}

        /// Получение данных (в формате CSR) о верхнем треугольнике матрицы.
        /// \remarks Некоторые алгоритмы работающие с матрицами можно значительно ускорить, предоставив им информацию о
        /// внутренней структуре матрицы.
        const QVector<double>& ggu() const {return m_ggu;}

        /// Неполная LU факторизация матрицы.
        /// \sa Метод конечных элементов для решения скалярных и векторных задач : учеб. пособие / Ю.Г. Соловейчик, М.Э. Рояк,
        /// М.Г. Персова - Новосибирск: Изд-во НГТУ, 2007. - 896 с.
        /// \return Матрица в разреженно-строчном формате. Нижний треугольник и диагональ матрицы дают матрицу факторизации L, а
        /// верхний треугольник - матрицу факторизации U.
        CSRMatrix partFactorLU() const;

        /// Получение доступа к элементу матрицы A[i,j].
        /// \remarks Синтаксис нестандартный, но это позволяет сделать доступ к элементу матрицы максимально быстрым и логически ясным.
        double& operator()(int i, int j);

        /// Перегруженная функция.
        const double& operator()(int i, int j) const;

    private:
        int m_Dimension;        ///< Размерность матрицы.
        QVector<int> m_igl;      ///< Информация о количестве ненулевых элементов в каждой строке матрицы.
        QVector<int> m_jgl;      ///< Информация о номерах столбцов ненулевых элементов матрицы.
        QVector<int> m_igu;
        QVector<int> m_ngu;
        QVector<double> m_di;   ///< Диагональ матрицы.
        QVector<double> m_ggl;  ///< Нижний треугольник матрицы.
        QVector<double> m_ggu;  ///< Верхний треугольник матрицы.
    };

    //============================================================================================================================
    //============================================================================================================================
    /// Умножение матрицы на вектор.
    /// \relates Vector
    /// \relates CSRMatrix
    CORE_EXPORT const Vector operator*(const CSRMatrix &matrix, const Vector &vector);
}

#endif // _LINAL_CSRMATRIX_
